﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
using System.Windows.Forms;


namespace GradientTrackBarControlLibrary
{
    /// <summary>
    /// GradientTrackBar - компонент редактирования 
    /// цветовой шкалы
    /// </summary>
    public partial class GradientTrackBar : UserControl
    {
        //событие сохранения шкалы
        public delegate void OnApply(GradientTrackBar iTracBar);
        public event OnApply Apply;
        public delegate void OnScale(GradientTrackBar iTracBar);
        public event OnScale Scale;


        List<Gradient> _Gradient = new List<Gradient>();  // градиентные заливки
        float _F1 = 0;
        float _F2 = 1;
        float _F3 = 2;
        float _F4 = 3;

         public float F1
        {
            get { return _F1;  }
            set { _F1 = value; F1TextBox.Text = String.Format("{0:E2}", _F1);  }
        }
        public Color Color1
        {
            get { return _Gradient[0].StartColor; }
        }

        public float F2
        {
            get { return _F2; }
            set { _F2 = value; F2TextBox.Text = String.Format("{0:E2}", _F2); }
        }
        public Color Color2
        {
            get { return _Gradient[1].StartColor; }
        }

        public float F3
        {
            get { return _F3; }
            set { _F3 = value; F3TextBox.Text = String.Format("{0:E2}", _F3); }
        }
        public Color Color3
        {
            get { return _Gradient[2].StartColor; }
        }

        public float F4
        {
            get { return _F4; }
            set { _F4 = value; F4TextBox.Text = String.Format("{0:E2}", _F4); }
        }
        public Color Color4
        {
            get { return _Gradient[2].EndColor; }
        }

        public int F1F2Steps
        {
            get { return _Gradient[0].Steps; }        
        }

        public int F2F3Steps
        {
            get { return _Gradient[1].Steps; }
        }

        public int F3F4Steps
        {
            get { return _Gradient[2].Steps; }
        }

        public List<Color> ColorArray
        {
            get
            {
                List<Color> l = new List<Color>();
                for(int i=0; i<_Gradient.Count; i++)
                {
                    Color c1 = _Gradient[i].StartColor;
                    Color c2 = _Gradient[i].EndColor;

                    double stepR = (double)(c2.R - c1.R) / _Gradient[i].Steps;
                    double stepG = (double)(c2.G - c1.G) / _Gradient[i].Steps;
                    double stepB = (double)(c2.B - c1.B) / _Gradient[i].Steps;

                    for (int j = 0; j < _Gradient[i].Steps; j++)
                    {
                        Color c = Color.FromArgb((int)(c1.R + stepR * j), (int)(c1.G + stepG * j), (int)(c1.B + stepB * j));
                        l.Add(c);
                    }
                }
                
                return l;
            }
        }
        
        public List<double> FunctionArray
        {
            get
            {
                List<double> l = new List<double>();
                double f1 = F1;
                double step = (F2 - F1) / F1F2Steps;

                for (int i = 0; i < F1F2Steps; i++)
                {
                    double f = f1 + step * i;
                    l.Add(f);
                }

                f1 = F2;
                step = (F3 - F2) / F2F3Steps;

                for (int i = 0; i < F2F3Steps; i++)
                {
                    double f = f1 + step * i;
                    l.Add(f);
                }

                f1 = F3;
                step = (F4 - F3) / F3F4Steps;

                for (int i = 0; i < F3F4Steps; i++)
                {
                    double f = f1 + step * i;
                    l.Add(f);
                }
                l.Add(F4);
                return l;
            }
        }

        //------------------------------------------------------------------------------------------------------------------
        // градиентная заливка между двумя треугольниками
        public partial class Gradient
        {
            Color _StartColor;
            Color _EndColor;
            int   _Steps;
            
            RectangleF _rect;

            public Gradient(RectangleF rect, Color StartColor, Color EndColor, int Steps)
            {
                _StartColor = StartColor;
                _EndColor = EndColor;
                _rect = rect;
                _Steps = Steps;
            }

            public void SetRect(RectangleF rect)
            {
                _rect = rect;
            }

            public void SetColor(Color StartColor, Color EndColor)
            {
                _StartColor = StartColor;
                _EndColor = EndColor;                
            }

            public Color StartColor
            {
                get { return _StartColor;  }
                set { _StartColor = value; }
            }


            public Color EndColor
            {
                get { return _EndColor; }
                set { _EndColor = value; }
            }

            public int Steps
            {
                get { return _Steps; }
                set { _Steps = value; } 
            }

            public void Draw(Graphics g)
            {
                //just linear gradien
                if(_Steps > _rect.Height)
                {
                    g.FillRectangle(new LinearGradientBrush(_rect, _StartColor, _EndColor, LinearGradientMode.Vertical), _rect);
                    return;
                }


                float step = (_rect.Bottom - _rect.Top) / _Steps;
                
                RectangleF drawing_rect = _rect;
                drawing_rect.Height = step;

                Color c = _StartColor;
                
                //for colors
                int rstep = (_EndColor.R - _StartColor.R)/_Steps;
                int gstep = (_EndColor.G - _StartColor.G) / _Steps;
                int bstep = (_EndColor.B - _StartColor.B) / _Steps;
                int astep = (_EndColor.A - _StartColor.A) / _Steps;

                //draw rectangle
                for(int i=0; i<_Steps; i++)
                {
                    drawing_rect.Y = _rect.Y + i*step;

                    c = Color.FromArgb(_StartColor.A + i * astep, _StartColor.R + i * rstep, _StartColor.G + i * gstep, _StartColor.B + i * bstep);
                    g.FillRectangle(new SolidBrush(c), drawing_rect);
                }
            }

            public enum Zones{ TOP, BOTTOM, NONE }
            public Zones TestClick(Point p)
            {
                //in hot zone
                if (p.X < _rect.Right && p.X > _rect.Left)
                {
                    if (p.Y > _rect.Top && p.Y < _rect.Top + 5) return Zones.TOP;
                    if (p.Y > _rect.Bottom - 5 && p.Y < _rect.Bottom) return Zones.BOTTOM;
                }
                return Zones.NONE;
            }
        }


        //-----------------------------------------------------------------------------------------------------------------------------
        public GradientTrackBar()
        {
            InitializeComponent();
            GradientTrackBar_Resize(null, null);

            F1 = 0;
            F2 = 1;
            F3 = 2;
            F4 = 3;
        }

        private void GradientTrackBar_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            foreach (Gradient x in _Gradient)
                x.Draw(g);
        }


        //------------------------------------------------------------------------------------------------------------------------
        private void ApplyBtn_Click(object sender, EventArgs e)
        {
            try
            {
                _F1 = float.Parse(F1TextBox.Text);
                _F2 = float.Parse(F2TextBox.Text);
                _F3 = float.Parse(F3TextBox.Text);
                _F4 = float.Parse(F4TextBox.Text);

                int steps1 = int.Parse(Steps1TextBox.Text);
                int steps2 = int.Parse(Steps2TextBox.Text);
                int steps3 = int.Parse(Steps3TextBox.Text);

                _Gradient[2].Steps = steps1;
                _Gradient[1].Steps = steps2;
                _Gradient[0].Steps = steps3;

                if (_F1 < _F2 && _F2 < _F3 && _F3 < _F4)
                {
                    Apply(this);
                    this.Invalidate();
                }
                else
                {
                    MessageBox.Show("Must be F1 < F2 < F3 < F4", "Color scale error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            catch (System.Exception exc)
            {
                MessageBox.Show("All Fs in color scale must be float\n all steps must be integer", "Color scale error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            
        }

        private void GradientTrackBar_MouseUp(object sender, MouseEventArgs e)
        {
          for (int i = 0; i < _Gradient.Count; i++)
            {
                Gradient.Zones z = _Gradient[i].TestClick(e.Location);
                if (z != Gradient.Zones.NONE)
                {
                    ColorDialog dlg = new ColorDialog();
                    if (dlg.ShowDialog(this) == DialogResult.OK)
                    {
                        if (z == Gradient.Zones.BOTTOM)
                        {
                            _Gradient[i].EndColor = dlg.Color;
                            if(i != _Gradient.Count-1)
                            {
                                _Gradient[i+1].StartColor = dlg.Color;
                            }
                        }
                        else if (z == Gradient.Zones.TOP)
                        {
                            _Gradient[i].StartColor = dlg.Color;
                            if (i != 0)
                            {
                                _Gradient[i - 1].EndColor = dlg.Color;
                            }
                        }
                    }

                    this.Invalidate();
                    return;
                }
            }
        }

        private void GradientTrackBar_Resize(object sender, EventArgs e)
        {
            _Gradient.Clear();

            int h = (this.Height - 20) / 3;
            int w = 50;
            _Gradient.Add(new Gradient(new RectangleF(10, 20, w, h), Color.Red, Color.Yellow, 10));
            _Gradient.Add(new Gradient(new RectangleF(10, 10 + h, w, h), Color.Yellow, Color.Aqua, 10));
            _Gradient.Add(new Gradient(new RectangleF(10, 10 + 2 * h, w, h), Color.Aqua, Color.Blue, 10));


            //rearrange textboxes
            F1TextBox.Top = this.Height - F1TextBox.Height - 5;
            F1TextBox.Left = 10 + w + 4;

            F2TextBox.Top = 10 + 2 * h - F1TextBox.Height / 2;
            F2TextBox.Left = 10 + w + 4;

            F3TextBox.Top = 10 + h - F1TextBox.Height / 2;
            F3TextBox.Left = 10 + w + 4;

            F4TextBox.Top = 17;
            F4TextBox.Left = 10 + w + 4;

            //steps
            Steps1TextBox.Top = (F1TextBox.Top + F2TextBox.Top) / 2;
            Steps1TextBox.Left = F1TextBox.Right;

            Steps2TextBox.Top = (F3TextBox.Top + F2TextBox.Top) / 2;
            Steps2TextBox.Left = F1TextBox.Right;

            Steps3TextBox.Top = (F3TextBox.Top + F4TextBox.Top) / 2;
            Steps3TextBox.Left = F1TextBox.Right;

            //rearrange labels
            FLabel.Top = 0;
            FLabel.Left = 10 + w + 6;

            StepLabel.Top = 0;
            StepLabel.Left = F1TextBox.Right + 2;


            ApplyBtn.Top = this.Height - ApplyBtn.Height;
            ApplyBtn.Left = this.Width - ApplyBtn.Width;

            BtnScale.Top = ApplyBtn.Top - BtnScale.Height;
            BtnScale.Left = ApplyBtn.Left;
        }

        private void BtnScale_Click(object sender, EventArgs e)
        {
            Scale(this);
        }
        

        public override string ToString()
        {
            return  F1.ToString() + "|" +
                    F2.ToString() + "|" +
                    F3.ToString() + "|" +
                    F4.ToString() + "|" +
                    _Gradient[0].StartColor.ToArgb().ToString() + "|" +
                    _Gradient[1].StartColor.ToArgb().ToString() + "|" +
                    _Gradient[2].StartColor.ToArgb().ToString() + "|" +
                    _Gradient[2].EndColor.ToArgb().ToString() + "|" +
                    _Gradient[0].Steps.ToString() + "|" +
                    _Gradient[1].Steps.ToString() + "|" +
                    _Gradient[2].Steps.ToString();
                   
        }

        public void FromString(string iString)
        {
            try
            {
                string[] arr = iString.Split('|');
                
                
                //parse string
                F1 = float.Parse(arr[0]);
                F2 = float.Parse(arr[1]);
                F3 = float.Parse(arr[2]);
                F4 = float.Parse(arr[3]);

                _Gradient[0].StartColor = Color.FromArgb(int.Parse(arr[4]));
                _Gradient[0].EndColor = _Gradient[1].StartColor = Color.FromArgb(int.Parse(arr[5]));
                _Gradient[1].EndColor = _Gradient[2].StartColor = Color.FromArgb(int.Parse(arr[6]));
                _Gradient[2].EndColor = Color.FromArgb(int.Parse(arr[4]));

                _Gradient[0].Steps = int.Parse(arr[8]);
                _Gradient[1].Steps = int.Parse(arr[9]);
                _Gradient[2].Steps = int.Parse(arr[10]);

                ApplyBtn_Click(this, null);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Can't load color scale", "Color scale", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}